home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / sml_nj / 93src.lha / src / debug / exec.sml < prev    next >
Encoding:
Text File  |  1993-01-27  |  16.0 KB  |  459 lines

  1. (* DebugExec
  2.  
  3.    This module supports basic user code execution. It coordinates
  4.    and hides the functions of DebugKernel and the various history-maintaining
  5.    subsystems (static,io,signals,exec). Provides for:
  6.    - querying current state when stopped
  7.    - initializing instrumented user code for execution under debugger control.
  8.    - basic record and replay operations
  9.    - CTRL/C handling
  10.  
  11.    This module can be used directly by higher-level commands, or via
  12.    DebugRun.
  13.  
  14. *)
  15.  
  16. signature DEBUG_EXEC =
  17. sig
  18.   type time
  19.   datatype onNoise = QUIET | NOISY | BREAK of (unit->bool)
  20.       (* as in DebugKernel *)
  21.   val initialTime : time ref
  22.       (* time of STARTev for current outermost comp unit. *)
  23.   val finalTime: time ref
  24.       (* time of termination of currently outermost comp unit, or
  25.          infinity if unknown. *)
  26.   val blockingExn: exn option ref
  27.       (* exception blocking current outermost comp unit, if any. *)
  28.   (* State manipulation functions *)
  29.   type state (* ={time:time,
  30.               cont:userCont,
  31.           evData:evData,
  32.           depth:int,
  33.           memories:doers list (* for each history *)} *)
  34.   val currentState : unit -> state
  35.   val restoreState: (state * onNoise) -> unit
  36.   val zeroState: state  (* the absolutely initial state *)
  37.  
  38.   (* Querying elements of current state.  These are defined for convenience. *)
  39.   val currentTime: unit -> time
  40.   val currentEvn: unit -> DebugStatic.evn
  41.   val currentLbt: unit -> time
  42.   val currentArgs: unit -> System.Unsafe.object list
  43.   val knownTime: unit -> time
  44.  
  45.   (* Comp Unit initialization and premature termination. *)
  46.   datatype 'a result =
  47.       NORMAL of 'a
  48.     | EXCEPTION of exn
  49.     | ABORT 
  50.     | INTERRUPT
  51.   val setCompUnit: (unit -> 'a) ->
  52.                    (unit -> System.Unsafe.object vector) -> 
  53.                    System.Unsafe.object vector result
  54.   val abortCompUnit: unit ->  'a
  55.   val interruptCompUnit: unit -> 'a 
  56.   val exceptionCompUnit: exn -> 'a
  57.  
  58.   (* Interpolation *)
  59.   val setInterpolatedUnit: (unit -> unit) -> unit
  60.  
  61.   (* Interpolation and signal actions. *)
  62.   val forgetActions: unit -> unit
  63.  
  64.   (* Execution functions *)
  65.   val recordTo: (time * onNoise) -> bool
  66.   val recordRest: unit -> 'a 
  67.   val replayTo: (time * onNoise) -> bool
  68.  
  69.   (* Query functions *)
  70.   val inCompUnit: unit -> bool
  71.  
  72.   (* CTRL/C handling *)
  73.   val pendingInterrupt: bool ref
  74.   val setIntHand: unit -> unit
  75.   val resetIntHand: unit -> unit
  76. end
  77.  
  78. structure DebugExec : DEBUG_EXEC =
  79. struct
  80.   open DebugUtil DebugStatic DebugKernel
  81.   structure U = System.Unsafe
  82.   (* Log for all actions. *)
  83.   structure Log = TimedLog(type entry = action)
  84.   val actionMark = Log.new()
  85.   val zeroActionMark = Log.copyMark actionMark
  86.   val _ = (Log.append actionMark nullAction  (* at time 0 *) ;
  87.        Log.resetMark actionMark zeroActionMark)
  88.   fun nextAction() : action * time =
  89.       (* Return action for current time (possibly null) and time of first
  90.          logged action after current time. *)
  91.       let val (time,action) = Log.next actionMark
  92.                               handle Log.Log => (infinity,nullAction)
  93.       in if currentTime() = time then
  94.            (Log.advance actionMark;
  95.         let val (time',_) = Log.next actionMark
  96.                           handle Log.Log => (infinity,nullAction)
  97.         in dbgprint (implode["*act ",
  98.                  makestring (currentTime()), " T ",
  99.                  makestring time',"\n"]);
  100.            (action,time')
  101.         end)
  102.          else (dbgprint (implode["*act ",
  103.                 makestring (currentTime()), " F ",
  104.                 makestring time,"\n"]);
  105.            (nullAction,time))
  106.       end
  107.   fun rememberActions () =
  108.       let val savedMark = Log.copyMark actionMark
  109.       fun reset _ = Log.resetMark actionMark savedMark
  110.       in {redo=reset,undo=reset}
  111.       end
  112.  
  113.   
  114.   (* Master list of subsystem states *)
  115.   val remembers =
  116.       [DebugStatic.rememberEvnTimes,
  117.        DebugStore.remember,
  118.        DebugIO.remember,
  119.        DebugSignals.remember,  
  120.        rememberActions]
  121.  
  122.   val pendingInterpolation : action option ref = ref NONE
  123.   val initialTime : time ref = ref 0
  124.   val blockingExn : exn option ref = ref NONE
  125.   val finalTime: time ref = ref 0
  126.   val compUnitDepth: int ref = ref 0
  127.   fun inCompUnit () = (!compUnitDepth > 0)
  128.  
  129.   (* Current state.
  130.      (The implementation at this level is very stateful, because some
  131.      of the history mechanisms must deal with a large current state,
  132.      and it would be quite inefficient to copy this in and out 
  133.      all the time in order to maintain functional cleanliness. *)
  134.  
  135.   (* Clients may wish to separate out elements of, e.g., evData,
  136.      for efficiency. *)
  137.   type state ={time:time,
  138.            cont:userCont,
  139.            evData:evData,
  140.            depth:int,
  141.            memories:doers list (* for each history *)}
  142.  
  143.   fun currentState () : state =
  144.       {time=currentTime(),
  145.        cont=userCont(),
  146.        evData=currentEvData(),
  147.        depth= !compUnitDepth,
  148.        memories=map (fn remember => remember ()) remembers} 
  149.  
  150.   val zeroState = currentState()
  151.  
  152.   (* N.B. This must work correctly even when applied to an illicit current
  153.      state (later than the known time). *)
  154.   (*  -- some further checks on legitimacy of the new state might be nice... *)
  155.   fun restoreState ({time,cont,evData,memories,depth},
  156.             onNoise:onNoise): unit =
  157.     (dbgprint ("*r " ^ (makestring time) ^ "\n");
  158.      let val oldTime = currentTime() 
  159.      in
  160.        if time <> oldTime then
  161.      (if oldTime < time then
  162.         app  (fn ({redo,...}:doers) => redo onNoise) memories
  163.       else app (fn ({undo,...}:doers) => undo onNoise) memories;
  164.       setCurrentTime time;
  165.       setUserCont cont;
  166.           setCurrentEvData (evData);
  167.       compUnitDepth := depth)
  168.        else ()
  169.      end)
  170.  
  171.   (* Setting up thunks to run. *)
  172.  
  173.   val currentTime = DebugKernel.currentTime
  174.   fun currentEvn () = #evn(currentEvData())
  175.   fun currentLbt () = #lbt(currentEvData())
  176.   fun currentArgs () = #args(currentEvData())
  177.   val knownTime = DebugKernel.knownTime
  178.  
  179.   val lastState : state ref = ref (zeroState)
  180.   datatype 'a result =
  181.       NORMAL of 'a
  182.     | EXCEPTION of exn
  183.     | ABORT 
  184.     | INTERRUPT
  185.   val resultCont: U.object vector result cont ref = ref(makeCont "resultCont")
  186.  
  187.   fun reset() = 
  188.       (compUnitDepth := 0;
  189.        restoreState(!lastState,QUIET);
  190.        resetKnownTime();
  191.        finalTime := knownTime())
  192.  
  193.  
  194.   (* Routine setCompUnit sets up instrumented code to run, by 
  195.      creating an action to be invoked when user code is re-entered.
  196.      The argument is a compilation unit.
  197.      After setting up the code to run, invokes debugMonitor argument.
  198.      At this point, the other exec routines can be run from monitor 
  199.      commands to record,replay,etc. debugMonitor should return by
  200.      calling a suitable termination command, such as completeCompUnit() 
  201.      or abortCompUnit().
  202.      After recordRest(), abortCompUnit(), or interruptCompUnit()
  203.      has been successfully called, or an uncaught exception is raised,
  204.      setCompUnit returns with result NORMAL, ABORT, INTERRUPT,
  205.      or EXCEPTION, respectively.  On abnormal returns, the current
  206.      state and time will be reset to what they were before the call to
  207.      setCompUnit. It is the caller's responsibility to destroy any 
  208.      copies of the state that refer to an abnormally terminated unit. *)
  209.   fun setCompUnit debugMonitor
  210.                   (f:unit -> U.object vector) : U.object vector result =
  211.     (assert(not (inCompUnit()), "Exec.setCompUnit");
  212.      let fun runUnit () : unit =
  213.       (if not(inCompUnit()) then
  214.          inc compUnitDepth
  215.        else () (* unit is being invoked as an action at tail of 
  216.               previous unit *);
  217.        let val result = f() handle exn => 
  218.                           (exceptionCompUnit exn;
  219.                    debugPanic "returned from exceptionCompUnit")
  220.        in dec compUnitDepth;
  221.           advanceKnownTime();
  222.           finalTime := knownTime();
  223.           throw (!resultCont) (NORMAL result)
  224.        end)
  225.      in dbgprint ("*init \n");
  226.         lastState := currentState();
  227.     finalTime := infinity;
  228.     blockingExn := NONE;
  229.     callcc (fn cont => (resultCont := cont;
  230.                 initialTime := currentTime() + 1;
  231.                 Log.append actionMark runUnit;
  232.                 continue(!initialTime,RECORD QUIET,runUnit);
  233.                 assert (knownTime() = !initialTime,
  234.                     "Exec.setCompUnit 2");
  235.                 (* N.B. Must be normal result, ignoring
  236.                  possible interrupt. *)
  237.                 debugMonitor(); 
  238.                 debugPanic "returned from debugMonitor" ))
  239.     before ignore()
  240.      end)
  241.  
  242.   and exceptionCompUnit exn =
  243.       (pseudoEvent{evn=pseudoEvn UNCAUGHTev,
  244.            forced=true,
  245.            args=[U.cast exn]};
  246.        reset();
  247.        throw (!resultCont) (EXCEPTION exn))
  248.  
  249.   fun abortCompUnit() = 
  250.       (reset();
  251.        throw (!resultCont) ABORT)
  252.  
  253.   fun interruptCompUnit() = 
  254.       (reset();
  255.        throw (!resultCont) INTERRUPT)
  256.  
  257.   (* An interpolated comp unit is set to run as an action starting at
  258.      the current time. When the interpolation returns normally, 
  259.      the previous code will be resumed.
  260.      Setting an interpolation removes all previous interpolations set
  261.      at the current or any later time.  It is the caller's responsibility to
  262.      destroy any copies of the state that refer to any later time.
  263.      On abnormal results the current state and time will be reset to what 
  264.      they were before the *outer-level call* to setCompUnit, and the
  265.      *outer-level* abnormal return will be made immediately. *)
  266.   fun setInterpolatedUnit (f:unit -> unit) : unit =
  267.       (assert (inCompUnit(),"Exec.interpolateCompUnit 0");
  268.        assert (currentTime() < !finalTime,"Exec.interpolateCompUnit 1");
  269.        let fun runUnit () : unit =
  270.        (inc compUnitDepth;
  271.         DebugSignals.permitSignals false;
  272.         f();
  273.         DebugSignals.permitSignals true;
  274.         dec compUnitDepth)
  275.        in dbgprint ("*initInterp " ^ (makestring (!compUnitDepth)) ^ "\n");
  276.        finalTime := infinity;
  277.       blockingExn := NONE;
  278.       resetKnownTime();
  279.       pendingInterpolation := SOME runUnit  (* replaces any previous *)
  280.        end)
  281.  
  282.   fun getInterpolation() : action option =
  283.       case !pendingInterpolation of
  284.     SOME action => (pendingInterpolation := NONE;
  285.             SOME action)
  286.       | NONE => NONE
  287.  
  288.   fun forgetActions() : unit  =
  289.      (pendingInterpolation := NONE;
  290.       DebugSignals.forgetSignals())
  291.  
  292.   (* CTRL/C (SIGINT) handling. *)
  293.   val pendingInterrupt = ref false
  294.   local
  295.     open System.Signals
  296.     type handler = (int * unit cont) -> unit cont
  297.     val handlerSet = ref false
  298.     val normalHandOpt = ref (NONE:handler option)
  299.     fun debugHand (cnt,cont) =
  300.     (if not(!pendingInterrupt) then
  301.        (pendingInterrupt := true;
  302.           setTargetTime (currentTime() + 1))
  303.      else ();
  304.      cont)
  305.   in 
  306.     fun setIntHand () =
  307.     if not (!handlerSet) then
  308.          (normalHandOpt := inqHandler(SIGINT);
  309.           setHandler(SIGINT,SOME(debugHand));
  310.           handlerSet := true;
  311.                 pendingInterrupt := false)
  312.     else ()
  313.  
  314.     fun resetIntHand () =
  315.     if !handlerSet then
  316.       (setHandler(SIGINT,!normalHandOpt);
  317.        handlerSet := false)
  318.     else () (* debugPanic "resetIntHand" *)
  319.   end
  320.  
  321.   (* Fundamental execution functions. *)
  322.   (* Return conditions:
  323.      When we return from recordTo or replayTo, at *least* one of 
  324.      the following will be true:
  325.       (a) We reached target, in which case currentTime = target.
  326.       (b) We hit end of program or interpolation.
  327.       (c) We hit an uncaught exception.
  328.       (d) We hit a noise event with onNoise=BREAK (true condition). 
  329.       (e) CTRL/C was hit, in which case pendingInterrupt will be true. 
  330.       (f) A halting signal occurred during recording, in which case 
  331.                 DebugSignals.deliverableHalting will return true and
  332.         deliverableSignal will return the signal.
  333.      Note that (b),(c),(d) are mutually exclusive, but (a),(e), and
  334.      (f) can occur in combination with any of the others and/or each
  335.      other. Note also that on replayTo, (b) and (c) can be true only
  336.      if the target argument = knownTime(), and (f) cannot occur.
  337.  
  338.      The distinctions among (b),(c), and (d) are important for setting
  339.      finalTime and blockingExn, but are not generally of interest to
  340.      callers.  So for right now, we return just a boolean, which will
  341.      be false iff one of (b),(c),(d), occured.  Callers must still
  342.      check for (e) by consulting pendingInterrupt and, when recording, 
  343.      for (f) by calling deliverableHalting().
  344.     
  345.      The following datatype is used internally, and may prove useful
  346.      externally in future. *)
  347.  
  348.   datatype execResult = 
  349.       EXEC_NORMAL | EXEC_END | EXEC_EXN of exn | EXEC_NOISE | EXEC_INTERP
  350.  
  351.   (* Analyse current state immediately after returning from execution.
  352.      Not for export. *)
  353.   fun execResult onNoise : execResult =
  354.       case (hd o eventsFor) (currentEvn()) of
  355.     ENDev _ => 
  356.       if (knownTime() = currentTime()) then (* avoiding old ENDevs *)
  357.         if (!compUnitDepth > 1) then (* interpolation ENDev*)
  358.           EXEC_INTERP
  359.         else EXEC_END
  360.       else EXEC_NORMAL
  361.       | UNCAUGHTev => EXEC_EXN (U.cast (hd(currentArgs())))
  362.       | IOev => (case onNoise of
  363.               BREAK condition => if condition() then 
  364.                                EXEC_NOISE
  365.                      else EXEC_NORMAL
  366.           | _ => EXEC_NORMAL)
  367.       | _ => EXEC_NORMAL
  368.  
  369.   fun getAction () : action =
  370.       (* Check if an interpolation or signal handle action should be
  371.          injected before recording from the current time. 
  372.          Interpolations have priority over signals.
  373.      If an action is chosen, it is added to the action log. *)
  374.       case(case getInterpolation() of
  375.          SOME action => SOME action
  376.        | NONE => DebugSignals.handleSignal()) of
  377.     SOME action => (Log.append actionMark action;
  378.             (* N.B. implicitly zaps later log entries *)
  379.             action)
  380.       | NONE => nullAction
  381.  
  382.   fun recordTo (target:time,onNoise:onNoise) : bool =
  383.       (* Record up to target time, unless something else intervenes.
  384.          Check pending actions first.
  385.          For possible conditions on return, see above. *)
  386.       (dbgprint ("*rec " ^ makestring(target) ^ "\n");
  387.        (* assert (inCompUnit(),"Exec.recordTo"); *)
  388.        assert(currentTime() = knownTime(),"Exec.recordTo 2");
  389.        assert(target > knownTime(),"Exec.recordTo 3");
  390.        let fun go () = 
  391.          if currentTime() < !finalTime then
  392.            ((* jump into user program *)
  393.         continue(target,RECORD onNoise,getAction()); 
  394.         (* see why we stopped *)
  395.         case execResult onNoise of
  396.           EXEC_END =>
  397.             (finalTime := knownTime(); false)
  398.         | EXEC_INTERP => false
  399.         | EXEC_EXN exn => 
  400.             (finalTime := knownTime(); blockingExn := SOME exn; false)
  401.         | EXEC_NOISE => false
  402.         | EXEC_NORMAL =>
  403.             if target = knownTime() orelse !pendingInterrupt 
  404.                orelse DebugSignals.deliverableHalting () then
  405.               true
  406.             else (* presumably false noise break or 
  407.                 non-halting deliverable signal *)
  408.               go ())
  409.          else false
  410.        in go ()
  411.        end)
  412.  
  413.   fun recordRest ()  =
  414.       (* Record remainder of compilation unit, jumping back to caller
  415.          of the unit when done. 
  416.      This function does not return. *)
  417.       (dbgprint ("*recr\n");
  418.        (* assert (inCompUnit(),"Exec.recordRest 1"); *)
  419.        assert(currentTime() = knownTime(),"Exec.recordRest 2");
  420.        (* assume associated state appropriately set too *)
  421.        let fun go () =
  422.              ((* jump into user program *)
  423.           continue(infinity,RECORD NOISY,getAction()); 
  424.           if !pendingInterrupt then
  425.         interruptCompUnit() (* does not return *)
  426.           else (* presumably non-halting deliverable signal *)
  427.             go ())
  428.        in go() (* does not return *)
  429.        end)
  430.     
  431.   fun replayTo (target:time,onNoise:onNoise) : bool =
  432.     (* For possible conditions on return, see above.
  433.        If none of these conditions are met and we cannot reach 
  434.        target time for any reason, raise an exception.
  435.        We handle actions internally. *)
  436.       (dbgprint ("*repl " ^ makestring(target) ^ "\n");
  437.        (* assert (inCompUnit(),"Exec.replayTo 1"); *)
  438.        assert (target <= knownTime(),"Exec.replayTo 2");
  439.        assert (target > currentTime(),"Exec.replayTo 3");
  440.        (* assume state for currentTime appropriately set too *)
  441.        let fun loop() =
  442.        let val (action,nextActionTime) = nextAction()
  443.            val target' = min(target,nextActionTime)
  444.        in (currentTime() < !finalTime) 
  445.           andalso
  446.           (continue(target',REPLAY onNoise,action);
  447.            case execResult onNoise of
  448.          EXEC_NORMAL =>
  449.              (!pendingInterrupt)
  450.              orelse 
  451.              (currentTime() = target) 
  452.              orelse
  453.              loop()
  454.            | _ => false)
  455.        end
  456.        in loop()
  457.        end)
  458. end
  459.